package com.xbk.seckill.order.application.place.impl;

import cn.hutool.core.util.BooleanUtil;
import com.alibaba.fastjson.JSONObject;
import com.xbk.seckill.common.cache.distribute.DistributedCacheService;
import com.xbk.seckill.common.constants.SeckillConstants;
import com.xbk.seckill.common.exception.ErrorCode;
import com.xbk.seckill.common.exception.SeckillException;
import com.xbk.seckill.common.model.dto.SeckillGoodsDTO;
import com.xbk.seckill.common.model.message.TxMessage;
import com.xbk.seckill.common.utils.id.SnowFlakeFactory;
import com.xbk.seckill.dubbo.interfaces.goods.SeckillGoodsDubboService;
import com.xbk.seckill.mq.MessageSenderService;
import com.xbk.seckill.order.application.model.command.SeckillOrderCommand;
import com.xbk.seckill.order.application.place.SeckillPlaceOrderService;
import com.xbk.seckill.order.domain.model.entity.SeckillOrder;
import com.xbk.seckill.order.domain.service.SeckillOrderDomainService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.TimeUnit;

/**
 * 基于数据库防止库存超卖
 *
 * @author xiexu
 * @date 2025/02/06 17:25
 */
@Slf4j
@Service
// 当配置文件中配置了place.order.type=db时，才加载该类
@ConditionalOnProperty(name = "place.order.type", havingValue = "db")
public class SeckillPlaceOrderDbService implements SeckillPlaceOrderService {

    @DubboReference(version = "1.0.0", check = false)
    private SeckillGoodsDubboService seckillGoodsDubboService;

    @Autowired
    private SeckillOrderDomainService seckillOrderDomainService;

    @Autowired
    private MessageSenderService messageSenderService;

    @Autowired
    private DistributedCacheService distributedCacheService;

    /**
     * 下单操作
     *
     * @param userId              用户id
     * @param seckillOrderCommand 下单命令
     * @return 事务编号
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long placeOrder(Long userId, SeckillOrderCommand seckillOrderCommand) {
        // 获取商品
        SeckillGoodsDTO seckillGoods = seckillGoodsDubboService.getSeckillGoods(seckillOrderCommand.getGoodsId(),
                seckillOrderCommand.getVersion());
        // 检测商品信息
        this.checkSeckillGoods(seckillOrderCommand, seckillGoods);
        long txNo = SnowFlakeFactory.getSnowFlakeFromCache().nextId();
        boolean exception = false;
        try {
            // 获取商品库存
            Integer availableStock = seckillGoodsDubboService.getAvailableStockById(seckillOrderCommand.getGoodsId());
            // 库存不足
            if (availableStock == null || availableStock < seckillOrderCommand.getQuantity()) {
                throw new SeckillException(ErrorCode.STOCK_LT_ZERO);
            }
        } catch (Exception e) {
            exception = true;
            log.error("SeckillPlaceOrderDbService|下单异常|参数:{}|异常信息:{}", JSONObject.toJSONString(seckillOrderCommand),
                    e.getMessage());
        }
        // 发送事务消息
        messageSenderService.sendMessageInTransaction(
                this.getTxMessage(SeckillConstants.TOPIC_TX_MSG, txNo, userId, SeckillConstants.PLACE_ORDER_TYPE_DB,
                        exception, seckillOrderCommand, seckillGoods, 0, seckillOrderCommand.getOrderTaskId()),
                null);
        return txNo;
    }

    /**
     * 保存订单
     *
     * @param txMessage 事务消息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveOrderInTransaction(TxMessage txMessage) {
        try {
            Boolean submitTransaction = distributedCacheService.hasKey(
                    SeckillConstants.getKey(SeckillConstants.ORDER_TX_KEY, String.valueOf(txMessage.getTxNo())));
            if (BooleanUtil.isTrue(submitTransaction)) {
                log.info("saveOrderInTransaction|已经执行过本地事务|{}", txMessage.getTxNo());
                return;
            }
            // 构建订单
            SeckillOrder seckillOrder = this.buildSeckillOrder(txMessage);
            // 保存订单
            seckillOrderDomainService.saveSeckillOrder(seckillOrder);
            // 保存事务日志
            distributedCacheService.put(
                    SeckillConstants.getKey(SeckillConstants.ORDER_TX_KEY, String.valueOf(txMessage.getTxNo())),
                    txMessage.getTxNo(), SeckillConstants.TX_LOG_EXPIRE_DAY, TimeUnit.DAYS);
        } catch (Exception e) {
            log.error("saveOrderInTransaction|异常|{}", e.getMessage());
            distributedCacheService.delete(
                    SeckillConstants.getKey(SeckillConstants.ORDER_TX_KEY, String.valueOf(txMessage.getTxNo())));
            throw e;
        }
    }

}
